/*
 * Copyright 2011-2022 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0.
 * See `LICENSE` in the project root for license information.
 */

package me.ijleex.platform.uploadfile.service.impl;

import java.time.LocalDateTime;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;

import me.ijleex.platform.framework.entity.Result;
import me.ijleex.platform.uploadfile.autoconfigure.UploadFileProperties;
import me.ijleex.platform.uploadfile.entity.UploadFile;
import me.ijleex.platform.uploadfile.mapper.UploadFileMapper;
import me.ijleex.platform.uploadfile.service.IUploadFileService;
import me.ijleex.platform.uploadfile.util.LocalUploadFileUtil;

/**
 * 上传文件 服务层实现
 *
 * @author liym
 * @since 2019-10-30 20:48:22 By 'codegen'
 */
@Service
public class UploadFileServiceImpl implements IUploadFileService {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final UploadFileMapper mapper;

    private final String localPath;
    private final String pathPattern;

    public UploadFileServiceImpl(UploadFileMapper mapper, UploadFileProperties properties) {
        this.mapper = mapper;

        UploadFileProperties.LocalFs localFs = properties.getLocalFs();
        this.localPath = localFs.getPath();
        this.pathPattern = localFs.getPathPattern();

        if (StringUtils.isAnyBlank(this.localPath, this.pathPattern)) {
            logger.warn("上传文件配置项 upload-file.local-fs.path、upload-file.local-fs.path-pattern 为空，上传文件将不可用。");
            return;
        }

        LocalUploadFileUtil.initLocalFsDir(this.localPath);
    }

    @Override
    public Result uploadFile(byte[] fileContent, String originalFilename, String contentType, long fileSize, String user) {
        return this.uploadFile(fileContent, originalFilename, contentType, fileSize, user, false);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Result uploadFile(byte[] fileContent, String originalFilename, String contentType, long fileSize, String adminId, boolean encrypt) {
        Result result = LocalUploadFileUtil.putFile(this.localPath, fileContent, originalFilename, contentType, fileSize, encrypt);
        if (!result.isOk()) {
            return result;
        }

        // 保存上传文件的信息
        UploadFile uploadFile = new UploadFile();
        uploadFile.setName(originalFilename);
        uploadFile.setPath(result.getData("path", "").toString());
        uploadFile.setType(contentType);
        uploadFile.setSize(fileSize);
        uploadFile.setState(encrypt ? 2 : 0);
        uploadFile.setRemarks("本地文件");
        uploadFile.setCreator(adminId);
        uploadFile.setCreateTime(LocalDateTime.now());
        try {
            int i = this.mapper.insert(uploadFile);
            if (i == 0) {
                logger.error("保存上传文件信息失败：{}", uploadFile);
                return Result.error("上传文件失败，请重试！");
            }
        } catch (DataAccessException e) {
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            logger.error("保存上传文件信息失败：" + uploadFile, e);
            return Result.custom(500, "上传文件失败，请联系管理员！");
        }
        String url = LocalUploadFileUtil.buildFileUrl(this.pathPattern, uploadFile.getPath());
        return result.addData("fileId", uploadFile.getId()).addData("url", url);
    }

    @Override
    public int deleteFileByIds(String ids) {
        return this.mapper.delete(ids);
    }

    @Override
    public int updateFile(UploadFile uploadFile) {
        uploadFile.setUpdateTime(LocalDateTime.now());
        return this.mapper.update(uploadFile);
    }

    @Override
    public List<UploadFile> getFileList(UploadFile uploadFile) {
        return this.mapper.selectList(uploadFile);
    }

    @Override
    public UploadFile getFileById(Long id) {
        return this.mapper.selectById(id);
    }

    /**
     * @see org.springframework.core.io.ResourceLoader#getResource(java.lang.String)
     * @since 2019-12-06 11:08
     */
    @Override
    public Result getFileByPath(String partPath) {
        if (StringUtils.isBlank(partPath)) {
            return Result.invalidArg("本地文件路径不能为空");
        }
        UploadFile uploadFile = this.mapper.selectByPath(partPath);
        if (uploadFile == null) {
            return Result.error("本地文件不存在：" + partPath);
        }
        try {
            Result result = Result.error("获取文件失败：" + partPath);
            if (uploadFile.getState() == 0) {
                String contentType = uploadFile.getType();
                result = LocalUploadFileUtil.getFile(this.localPath, partPath, null, contentType, 0L, false);
            } else if (uploadFile.getState() == 1) {
                result = Result.error("文件已失效");
            } else if (uploadFile.getState() == 2) {
                String fileName = uploadFile.getName();
                String contentType = uploadFile.getType();
                Long fileSize = uploadFile.getSize();
                result = LocalUploadFileUtil.getFile(this.localPath, partPath, fileName, contentType, fileSize, true);
            }
            return result;
        } catch (Exception e) {
            logger.error("getFileByPath error", e);
            return Result.error("获取文件失败：" + partPath);
        }
    }

    // ===

    /**
     * 配置文件传文件虚拟目录，用于根据路径获取文件（显示图片/文件，直接显示）
     *
     * <pre>
     * {@code <img src="/local-fs/5q/2f/ZLmITlpqLUBDlGhHyyHeCLFRp.jpg" style="height:250px;width:400px;" alt="image"/>}
     * </pre>
     *
     * @since 2019-07-22 11:47
     */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        if (StringUtils.isAnyBlank(this.localPath, this.pathPattern)) {
            logger.warn("上传文件配置项 upload-file.local-fs.path、upload-file.local-fs.path-pattern 为空，上传文件不可用。");
            return;
        }
        /// registry.addResourceHandler("/local-fs/**").addResourceLocations("file:D:/image/upload/");
        String location = this.localPath.endsWith("/") ? "file:" + this.localPath : "file:" + this.localPath + '/';
        registry.addResourceHandler(this.pathPattern).addResourceLocations(location);
    }

    /**
     * 支持 CORS 跨域访问
     *
     * @since 2019-07-24 15:34
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        if (StringUtils.isBlank(this.pathPattern)) {
            return;
        }
        // 配置允许跨域访问的路径，如 /local-fs/**
        registry.addMapping(this.pathPattern).allowedOrigins("*").allowedMethods("GET", "OPTIONS").allowCredentials(true).maxAge(3600L);
    }

}
